package files

import (
	"archive/zip"
	"errors"
	"fmt"
	"io"
	"io/fs"
	"os"
	"path/filepath"
	"strings"
)

const (
	copyBuffer = 1024
)

func IsNotExist(pathStr string) bool {
	_, err := os.Stat(pathStr)
	if err == nil {
		return false
	}
	return errors.Is(err, fs.ErrNotExist)
}

func IsExist(pathStr string) bool {
	_, err := os.Stat(pathStr)
	if err == nil {
		return true
	}
	return errors.Is(err, fs.ErrExist)
}

// 复制文件，可重新命名
func Copy2(srcFile, dstFile string, overWritting bool) error {
	// 判断源文件是否存在
	if IsNotExist(srcFile) {
		return errors.New("file " + srcFile + " is not exist, Please check or create it")
	}
	// 打开源文件
	src, err := os.Open(srcFile)
	defer src.Close()
	if err != nil {
		return err
	}
	// 打开目标文件，如果没有则创建
	if IsExist(dstFile) && !overWritting {
		return fmt.Errorf("目标文件%s已存在", dstFile)
	}
	dst, err := os.OpenFile(dstFile, os.O_CREATE|os.O_RDWR, 0644)
	defer dst.Close()
	if err != nil {
		return err
	}
	// 创建channel, 将读取到的文件内容写入管道中，目标文件读取管道中的内容，并写进目标文件
	bufferChan := make(chan []byte, copyBuffer)
	go func() {
		srcOffSet := int64(0)
		for {
			srcFileByte := make([]byte, 1024)
			readFileLength, readErr := src.ReadAt(srcFileByte, int64(srcOffSet))
			bufferChan <- srcFileByte[:readFileLength]
			if readErr != nil {
				break
			}
			srcOffSet += copyBuffer
		}
		close(bufferChan)
	}()
	// 写入文件内容
	dstOffSet := 0
	for fileByte := range bufferChan {
		dst.WriteAt(fileByte, int64(dstOffSet))
		dstOffSet += copyBuffer
	}
	return nil
}

// 删除文件
func RemoveFile(dstFile string) error {
	err := os.Remove(dstFile)
	if err != nil {
		return err
	}
	return nil
}

// 移动文件
func Move(srcFile, dstFile string, overWritting bool) error {
	if IsNotExist(srcFile) {
		return errors.New("源文件不存在：" + srcFile)
	}
	if IsExist(dstFile) {
		return errors.New("目标文件已存在: " + dstFile)
	}
	err := Copy(srcFile, dstFile, overWritting)
	if err != nil {
		return err
	}
	err = RemoveFile(srcFile)
	if err != nil {
		return err
	}
	return nil
}

// 文件是否有执行权限
func IsExecutable(dstFile string) bool {
	dstFileInfo, err := os.Lstat(dstFile)
	if err != nil {
		fmt.Println(err)
		return false
	}
	dstFileMode := dstFileInfo.Mode()
	fmt.Println(dstFileMode)
	execMode := fs.FileMode(0001)
	return dstFileMode&execMode != 0
}

// 判断是否是普通文件
func IsFile(dstFile string) bool {
	dstFileInfo, err := os.Lstat(dstFile)
	if err != nil {
		fmt.Println(err)
		return false
	}
	return dstFileInfo.Mode().IsRegular()
}

func IsDir(dst string) bool {
	dstFileInfo, err := os.Lstat(dst)
	if err != nil {
		fmt.Println(err)
		return false
	}
	return dstFileInfo.Mode().IsDir()
}

// 判断目录是否存在
func IsDirExist(dir string) bool {
	dirInfo, err := os.Lstat(dir)
	if err != nil {
		return false
	}
	return dirInfo.IsDir()
}

// 文件大小
func Size(dstFile string) (int64, error) {
	var fileSize int64
	file, err := os.Lstat(dstFile)
	if err != nil {
		return fileSize, err
	}
	return file.Size(), nil
}

// 读取普通文件
func ReadFile(dstFile string) (string, error) {
	var fileContent string
	file, err := os.OpenFile(dstFile, os.O_RDONLY, 0644)
	defer file.Close()
	if err != nil {
		return fileContent, err
	}
	offSet := int64(0)
	buffer := make([]byte, 1024)
	for {
		length, err := file.ReadAt(buffer, offSet)

		if errors.Is(io.EOF, err) || length <= 0 {
			fileContent += string(buffer[0:length])
			break
		}
		fileContent += string(buffer)
		offSet += copyBuffer
	}
	return fileContent, nil
}

// 创建文件目录
func Mkdir(dir string) error {
	if err := os.MkdirAll(dir, 0755); err != nil {
		return err
	}
	return nil
}

// 删除文件目录
func RemoveDir(dir string) error {
	if err := os.RemoveAll(dir); err != nil {
		return err
	}
	return nil
}

// 列出目录内容
func ListDir(dirname string, isRecur bool) ([]string, error) {
	// dirname: 目录名称
	// isRescur: 是否递归
	var file_list []string
	root, err := filepath.Abs(dirname)
	if err != nil {
		return file_list, err
	}
	err = filepath.Walk(root, func(path string, info fs.FileInfo, err error) error {
		if err != nil {
			fmt.Printf("文件%q读取错误：%v\n", path, err)
		}
		if isRecur {
			file_list = append(file_list, path)
		} else {
			if filepath.Dir(path) == dirname {
				file_list = append(file_list, path)
			}
		}
		return nil
	})
	if err != nil {
		fmt.Printf("文件%q读取错误：%v\n", dirname, err)
		return nil, err
	}
	return file_list, err
}

// 复制目录
func CopyDir(src, dst string, verbose bool) error {
	src, outerr := filepath.Abs(src)
	if outerr != nil {
		return outerr
	}
	dst, outerr = filepath.Abs(dst)
	if outerr != nil {
		return outerr
	}
	if IsFile(src) {
		if err := Copy(src, dst, true); err != nil {
			return err
		}
	}
	if IsDir(src) {
		if !IsDirExist(dst) {
			if err := Mkdir(dst); err != nil {
				return err
			}
		}
		outerr = filepath.Walk(src, func(path string, info fs.FileInfo, err error) error {
			if err != nil {
				return err
			}
			newPath := strings.Replace(path, src, dst, 1)
			if verbose {
				fmt.Printf("%s --> %s\n", path, newPath)
			}
			if info.IsDir() {
				if !IsDirExist(newPath) {
					mkerr := os.Mkdir(newPath, 0755)
					if mkerr != nil {
						return mkerr
					}
				}
			}
			if info.Mode().IsRegular() {
				if cpErr := Copy(path, newPath, true); cpErr != nil {
					return cpErr
				}
			}
			return nil
		})
		if outerr != nil {
			return outerr
		}
	}

	return nil
}

func ZipList(src string) error {
	r, err := zip.OpenReader(src)
	if err != nil {
		return err
	}
	defer r.Close()
	for _, f := range r.File {
		fmt.Printf("Contents of %s\n", f.Name)
	}
	return nil
}

func UnZip(src, dst string) error {
	r, err := zip.OpenReader(src)
	if err != nil {
		return err
	}
	defer r.Close()
	for _, f := range r.File {
		extractFile := filepath.Join(dst, f.Name)
		fmt.Printf("%s --> %s\n", f.Name, extractFile)
		if f.FileInfo().IsDir() {
			if err := os.MkdirAll(extractFile, 0755); err != nil {
				return err
			}
		} else {
			extractDir := filepath.Dir(f.Name)
			dstDir := filepath.Join(dst, extractDir)
			if extractDir != "" && !IsDirExist(dstDir) {
				if err := os.MkdirAll(dstDir, 0755); err != nil {
					fmt.Printf("创建目录异常：%s\n", dstDir)
					return err
				}
				fmt.Printf("目录%s已创建", extractDir)
			}
			extractFileInfo, err := os.OpenFile(extractFile, os.O_CREATE|os.O_RDWR, 0644)
			if err != nil {
				return err
			}
			freader, err := f.Open()
			if err != nil {
				return err
			}
			_, err = io.Copy(extractFileInfo, freader)
			if err != nil {
				return err
			}
			freader.Close()
			extractFileInfo.Close()
		}
	}
	return nil
}

func Zip(dst string, src ...string) error {
	dstZip, err := os.OpenFile(dst, os.O_CREATE|os.O_RDWR, 0644)
	defer dstZip.Close()
	if err != nil {
		return err
	}
	w := zip.NewWriter(dstZip)
	for _, srcFile := range src {
		fmt.Printf("压缩文件%s\n", srcFile)
		if !IsExist(srcFile) {
			return fmt.Errorf("指定的文件或目录%s不存在", srcFile)
		}
		srcBase := filepath.Base(srcFile)
		srcDir := filepath.Dir(srcFile)
		if IsFile(srcFile) {
			f, err := w.Create(srcBase)
			if err != nil {
				return err
			}
			srcBytes, err := os.ReadFile(srcFile)
			if err != nil {
				return err
			}
			_, err = f.Write(srcBytes)
			if err != nil {
				return err
			}
		}
		if IsDir(srcFile) {
			outerr := filepath.Walk(srcFile, func(path string, d fs.FileInfo, err error) error {
				pathBase := strings.Replace(path, srcDir, "", 1)
				if d.Mode().IsRegular() {
					f, err := w.Create(pathBase)
					if err != nil {
						return err
					}
					pathBytes, err := os.ReadFile(path)
					if err != nil {
						return err
					}
					_, err = f.Write(pathBytes)
					if err != nil {
						return err
					}
				}
				return nil
			})
			if outerr != nil {
				return err
			}
		}
	}

	err = w.Close()
	if err != nil {
		return err
	}
	return nil
}

func Copy(src, dst string, overWritting bool) error {
	if IsFile(dst) && !overWritting {
		return fmt.Errorf("目标文件%s已存在", dst)
	}
	srcReader, err := os.OpenFile(src, os.O_RDONLY, 0644)
	if err != nil {
		return err
	}
	dstWriter, err := os.OpenFile(dst, os.O_CREATE|os.O_RDWR, 0644)
	if err != nil {
		return err
	}
	defer srcReader.Close()
	defer dstWriter.Close()
	r, w, err := os.Pipe()
	if err != nil {
		return err
	}
	buffer := make([]byte, copyBuffer)
	r = srcReader
	w = dstWriter
	for {
		n, err := r.Read(buffer)
		if n == 0 {
			break
		}
		if err != io.EOF && err != nil {
			return err
		}
		_, err = w.Write(buffer[0:n])
		if err != nil {
			return err
		}
	}
	return nil
}

func CatFile(src string) error {
	if !IsFile(src) {
		err := fmt.Errorf("未找到文件或目录")
		return err
	}
	srcFile, err := os.OpenFile(src, os.O_RDONLY, 0644)
	if err != nil {
		return err
	}
	r, w, err := os.Pipe()
	if err != nil {
		return err
	}
	buffer := make([]byte, copyBuffer)
	r = srcFile
	w = os.Stdout
	for {
		n, err := r.Read(buffer)
		if n == 0 {
			break
		}
		if err != nil {
			return err
		}
		w.Write(buffer[0:n])
	}

	defer srcFile.Close()
	return nil

}
